# 计算倾线值
import config
from draw_design_outline import DesignOutlineDrawer
import projecting_point
from adjustment_curve import getxy,get_the_route
import openpyxl
from matplotlib.patches import Circle
import matplotlib.pyplot as plt
import longitudinal_graph


def write_file(in_file_path=config.s_excel_path, out_file_path=config.bias_path):
    """写入倾线值表格"""
    in_file = openpyxl.load_workbook(in_file_path)
    in_sheet = in_file.active
    out_file = openpyxl.Workbook()
    out_sheet = out_file.active

    bias = Bias()
    s = None
    for i in range(1, in_sheet.max_row + 1):
        print('\r{}/{}'.format(i, in_sheet.max_row), end='')
        for j in range(1, 5):
            out_sheet.cell(row=i, column=j, value=in_sheet.cell(row=i, column=j).value)
        for j in range(8, 10):
            out_sheet.cell(row=i, column=j, value=in_sheet.cell(row=i, column=j).value)

        try:
            s = float(in_sheet.cell(row=i, column=2).value)
            if s > 50:
                break
        except TypeError:
            pass
        except ValueError:
            pass

        # 左
        try:
            y = float(in_sheet.cell(row=i, column=4).value)
            xs = bias.cal_x(s, y)  # x = [[实际x1, x2], [设计x1, x2]]
            out_sheet.cell(row=i, column=5, value=abs(xs[0][0]))
            out_sheet.cell(row=i, column=6, value=abs(xs[1][1]))
            out_sheet.cell(row=i, column=7, value=abs(xs[0][0]) - abs(xs[1][1]))
        except TypeError:
            print('\n{}:\ttype error'.format(s))
        except ValueError:
            pass
        # 右
        try:
            y = float(in_sheet.cell(row=i, column=9).value)
            xs = bias.cal_x(s, y)  # x
            out_sheet.cell(row=i, column=10, value=abs(xs[0][1]))
            out_sheet.cell(row=i, column=11, value=abs(xs[1][0]))
            out_sheet.cell(row=i, column=12, value=abs(xs[0][1]) - abs(xs[1][0]))
        except TypeError:
            print('\n{}:\ttype error'.format(s))
        except ValueError:
            pass
        # 上下
        if i % 4 == 3:
            try:
                ys = bias.cal_y(s)  # y = [[实际y1, y2], [设计y1, y2]]
                # 下
                out_sheet.cell(row=i, column=13, value=ys[0][1] / 1000)
                out_sheet.cell(row=i, column=14, value=ys[1][1] / 1000)
                out_sheet.cell(row=i, column=15, value=(abs(ys[0][1]) - abs(ys[1][1])) / 1000)
                # 上
                out_sheet.cell(row=i, column=17, value=ys[0][0] / 1000)
                out_sheet.cell(row=i, column=18, value=ys[1][0] / 1000)
                out_sheet.cell(row=i, column=19, value=(abs(ys[1][0]) - abs(ys[0][0])) / 1000)
            except ValueError:
                pass
            except TypeError:
                print('\n{}:\ttype error'.format(s))
    out_file.save(out_file_path)


def read_file(in_file_path=config.s_excel_path):
    """读取excel文件，计算倾限值"""
    print('===== reading files =====')
    in_file = openpyxl.load_workbook(in_file_path)
    in_sheet = in_file.active

    points = []
    s = None
    for i in range(1, in_sheet.max_row + 1):
        print('\r{}/{}'.format(i, in_sheet.max_row), end='')
        try:
            s = float(in_sheet.cell(row=i, column=2).value)
            if s > 410:
                break
        except TypeError:
            pass
        except ValueError:
            pass
        if type(s) is not float:
            continue
        # 左
        y = float(in_sheet.cell(row=i, column=4).value)
        points.append({
            'type': 'left',
            's': s,
            'y': y
        })
        # 右
        y = float(in_sheet.cell(row=i, column=9).value)
        points.append({
            'type': 'right',
            's': s,
            'y': y
        })
        # 上下
        if i % 4 == 3:
            points.append({
                'type': 'vertical',
                's': s,
            })

    print('\n===== calculating bias =====')
    bias = Bias()
    return bias.cal_bias(points)


def write_bias(in_file_path, out_file_path=config.bias_bias_path):
    """计算误差写入文件"""
    in_file = openpyxl.load_workbook(in_file_path)
    in_sheet = in_file.active
    bias_file = openpyxl.Workbook()
    bias_sheet = bias_file.active

    bias = Bias()
    s = None
    for i in range(1, in_sheet.max_row + 1):
        print('\r{}/{}'.format(i, in_sheet.max_row), end='')
        for j in range(1, 5):
            bias_sheet.cell(row=i, column=j, value=in_sheet.cell(row=i, column=j).value)
        for j in range(8, 10):
            bias_sheet.cell(row=i, column=j, value=in_sheet.cell(row=i, column=j).value)

        try:
            s = float(in_sheet.cell(row=i, column=2).value)
            if s > 50:
                break
        except TypeError:
            pass
        except ValueError:
            pass

        try:
            y = float(in_sheet.cell(row=i, column=4).value)
            xs = bias.cal_x(s, y)  # x = [[实际x1, x2], [设计x1, x2]]
            bias_sheet.cell(row=i, column=6, value=in_sheet.cell(row=i, column=6).value - abs(xs[1][1]))
            if xs[0][0] != 0:
                bias_sheet.cell(row=i, column=5, value=in_sheet.cell(row=i, column=5).value - abs(xs[0][0]))
                bias_sheet.cell(row=i, column=7,
                                value=in_sheet.cell(row=i, column=7).value - abs(xs[0][0]) + abs(xs[1][1]))
        except TypeError:
            print('\n{}:\ttype error'.format(s))
        except ValueError:
            pass
        try:
            y = float(in_sheet.cell(row=i, column=9).value)
            xs = bias.cal_x(s, y)  # x
            bias_sheet.cell(row=i, column=11, value=in_sheet.cell(row=i, column=11).value - abs(xs[1][0]))
            if xs[0][1] != 0:
                bias_sheet.cell(row=i, column=10, value=in_sheet.cell(row=i, column=10).value - abs(xs[0][1]))
                bias_sheet.cell(row=i, column=12,
                                value=in_sheet.cell(row=i, column=12).value - abs(xs[0][1]) + abs(xs[1][0]))
        except TypeError:
            print('\n{}:\ttype error'.format(s))
        except ValueError:
            pass
        if i % 4 == 3:
            try:
                ys = bias.cal_y(s)  # y = [[实际y1, y2], [设计y1, y2]]
                bias_sheet.cell(row=i, column=13, value=in_sheet.cell(row=i, column=13).value - ys[0][1] / 1000)
                bias_sheet.cell(row=i, column=14, value=in_sheet.cell(row=i, column=14).value - ys[1][1] / 1000)
                bias_sheet.cell(row=i, column=15,
                                value=in_sheet.cell(row=i, column=15).value - (ys[0][1] - ys[1][1]) / 1000)
                bias_sheet.cell(row=i, column=17, value=in_sheet.cell(row=i, column=17).value - ys[0][0] / 1000)
                bias_sheet.cell(row=i, column=18, value=in_sheet.cell(row=i, column=18).value - ys[1][0] / 1000)
                bias_sheet.cell(row=i, column=19,
                                value=in_sheet.cell(row=i, column=19).value - (ys[0][0] - ys[1][0]) / 1000)
            except ValueError:
                pass
            except TypeError:
                print('\n{}:\ttype error'.format(s))
    bias_file.save(out_file_path)


def draw_circles(s, canvas=None, oval1=None, oval2=None):
    """将s处的设计、实际圆画出来"""
    dod = DesignOutlineDrawer(config.section_R, config.h_file_path, config.bed_file_path, config.circle_s)
    x_cor, y_cor, A, B, R = projecting_point.main_function(s)
    A, B, R = A * 1000, B * 1000, R * 1000

    x0, y0 = dod.draw_outline(s)
    flag = getxy(s,2)[-1]
    print(flag)
    x0 *= flag
    y0 = longitudinal_graph.getZ(s, 2) * 1000 + y0
    # A *= -1
    B = longitudinal_graph.getZ(s, 2) * 1000 + B
    print('(x - {})^2 + (y - {})^2 = {}'.format(x0, y0, config.section_R * config.section_R))
    print('(x - {})^2 + (y - {})^2 = {}'.format(A, B, R * R))

    if canvas:
        if oval1:
            canvas.delete(oval1)
        if oval2:
            canvas.delete(oval2)
        x_center, y_center = canvas.winfo_width()/2, canvas.winfo_height()/2
        oval1 = canvas.create_oval(x_center-config.section_R/20, y_center-config.section_R/20,
                                   x_center+config.section_R/20, y_center+config.section_R/20, outline='red')
        x_center, y_center = x_center+A-x0, y_center+B-y0
        oval2 = canvas.create_oval(x_center-R/20, y_center-R/20, x_center+R/20, y_center+R/20, outline='green')
        return oval1, oval2
    else:
        cir1 = Circle(xy=(x0, y0), radius=config.section_R, alpha=0.5, facecolor='red')  # 设计圆，红色
        cir2 = Circle(xy=(A, B), radius=R, alpha=0.5, facecolor='green')  # 实际圆，绿色

        fig, ax = plt.subplots()
        ax.set_xbound(max(x0 - config.section_R, A - R) - 500, max(x0 + config.section_R, A + R) + 500)
        ax.set_ybound(max(y0 - config.section_R, B - R) - 500, max(y0 + config.section_R, B + R) + 500)
        ax.add_patch(cir1)
        ax.add_patch(cir2)
        plt.axis('equal')
        plt.show()


def write_circle_center(in_file_path, out_file_path):
    """将圆心写入文件"""
    in_file, out_file = openpyxl.load_workbook(in_file_path), openpyxl.Workbook()
    in_sheet, out_sheet = in_file.active, out_file.active

    dod = DesignOutlineDrawer(config.section_R, config.h_file_path, config.bed_file_path, config.circle_s)

    s, j = None, 2
    out_sheet.cell(row=1, column=1, value='里程')
    out_sheet.cell(row=1, column=2, value='实际圆x')
    out_sheet.cell(row=1, column=3, value='实际圆y')
    out_sheet.cell(row=1, column=4, value='实际圆R')
    out_sheet.cell(row=1, column=5, value='设计圆x')
    out_sheet.cell(row=1, column=6, value='设计圆y')
    out_sheet.cell(row=1, column=7, value='设计圆R')
    for i in range(2, in_sheet.max_row + 1, 4):
        print('\r{}/{}'.format(i, in_sheet.max_row), end='')
        try:
            s = float(in_sheet.cell(row=i, column=2).value)
            out_sheet.cell(row=j, column=1, value=s)
            if s > 410:
                break
        except TypeError:
            pass
        except ValueError:
            pass

        try:
            x_cor, y_cor, A, B, R = projecting_point.main_function(s)
            A, B, R = A * 1000, B * 1000, R * 1000

            x0, y0 = dod.draw_outline(s)
            flag = getxy(s)[-1]
            # print(flag)
            x0 *= flag
            y0 = longitudinal_graph.getZ(s) * 1000 + y0
            B = longitudinal_graph.getZ(s) * 1000 + B
            out_sheet.cell(row=j, column=2, value=A)
            out_sheet.cell(row=j, column=3, value=B)
            out_sheet.cell(row=j, column=4, value=R)
            out_sheet.cell(row=j, column=5, value=x0)
            out_sheet.cell(row=j, column=6, value=y0)
            out_sheet.cell(row=j, column=7, value=config.section_R)
            j += 1
        except TypeError:
            pass
        except ValueError:
            pass
    out_file.save(out_file_path)


class Bias:
    dod = None

    def __init__(self):
        self.dod = DesignOutlineDrawer(config.section_R, config.h_file_path, config.bed_file_path, config.circle_s)

    def cal_bias(self, points=None, start_s=None, end_s=None, canvas=None):
        """
        统计倾限值
        可以传入一个点数组或起始里程、中止里程
        如果传入里程，则每5米取一个截面，以道床高度+1600,0,-370,-870为左右取的8个点的纵坐标
        :param start_s: 起始里程
        :param end_s: 中止里程
        :param points: 点数组，一个点结构如下
        :param canvas: 将两个圆画到canvas上
        {
            'type': 'left'/'right'/'vertical',   点类型，左、右或中间两点
            's': float,                          点所在截面里程
            ['y': float]                         只有'left'和'right'两种类型有，点的高程
        }
        :return:
        bias    倾限的点数
        total   总共的点数
        """
        bias, total = 0, 0
        oval1, oval2 = None, None
        if points is None:
            for s in range(start_s, end_s, 5):
                # print('\r{}/{}'.format(s, end_s), end='')
                # print(s)
                _, bed = self.dod.find_curve(s)
                y_array = [(bed + 1600)/1000, bed/1000, (bed - 370)/1000, (bed - 870)/1000]
                if canvas:
                    oval1, oval2 = draw_circles(s, canvas, oval1, oval2)
                try:
                    ys = self.cal_y(s)  # y = [[实际y1, y2], [设计y1, y2]]
                    bias += (0 if abs(ys[0][1]) - abs(ys[1][1]) > 0 else 1)  # 下
                    bias += (0 if abs(ys[1][0]) - abs(ys[0][0]) > 0 else 1)  # 上
                    total += 2
                except projecting_point.NotACircleError:
                    total += 2
                except TypeError:
                    print('\n{}:\ttype error'.format(s))
                except ValueError:
                    print('\n{}:\tvalue error'.format(s))
                for y in y_array:
                    try:
                        xs = self.cal_x(s, y, False)  # x = [[实际x1, x2], [设计x1, x2]]
                        bias += (0 if abs(xs[0][0]) - abs(xs[1][1]) > 0 else 1)  # 左
                        bias += (0 if abs(xs[0][1]) - abs(xs[1][0]) > 0 else 1)  # 右
                        total += 2
                    except projecting_point.NotACircleError:
                        total += 2
                    except TypeError:
                        print('\n{}:\ttype error'.format(s))
                    except ValueError:
                        print('\n{}:\tvalue error'.format(s))
            if canvas:
                canvas.delete(oval1)
                canvas.delete(oval2)
        else:
            for index, point in enumerate(points):
                #print('\r{}/{}'.format(index + 1, len(points)), end='')
                s = point['s']
                # 上下
                if point['type'] == 'vertical':
                    try:
                        ys = self.cal_y(s)  # y = [[实际y1, y2], [设计y1, y2]]
                        bias += (0 if abs(ys[0][1]) - abs(ys[1][1]) > 0 else 1)  # 下
                        bias += (0 if abs(ys[1][0]) - abs(ys[0][0]) > 0 else 1)  # 上
                        total += 2
                    except TypeError:
                        print('\n{}:\ttype error'.format(s))
                    except ValueError:
                        print('\n{}:\tvalue error'.format(s))
                else:
                    try:
                        y = point['y']
                        xs = self.cal_x(s, y)  # x = [[实际x1, x2], [设计x1, x2]]
                        if point['type'] == 'left':  # 左
                            bias += (0 if abs(xs[0][0]) - abs(xs[1][1]) > 0 else 1)
                        else:  # 右
                            bias += (0 if abs(xs[0][1]) - abs(xs[1][0]) > 0 else 1)
                        total += 1
                    except TypeError:
                        print('\n{}:\ttype error'.format(s))
                    except ValueError:
                        print('\n{}:\tvalue error'.format(s))

        #print()
        return [bias, total]

    def cal_x(self, s, y, add_Z=True):
        """
        :param s: 里程
        :param y: 测试点纵坐标
        :param add_Z: 测试点是否需要加上高程，如果从表中读取数据需要加，如果理论计算则不用。默认需要加
        :return: [[实际圆x1,x2], [设计圆x1,x2]]
        """
        try:
            actual_x1, actual_x2 = projecting_point.calculate_coordinate_by_y(s, y, add_Z)
        except TypeError:
            actual_x1, actual_x2 = 0, 0
        except ValueError:
            actual_x1, actual_x2 = 0, 0
        finally:
            design_x1, design_x2 = self.dod.calculate_coordinate_by_y(s, y, add_Z)  # 根据y计算x

        return [
                [actual_x1, actual_x2],
                [design_x1, design_x2]
            ]

    def cal_y(self, s):
        try:
            actual_y1, actual_y2 = projecting_point.calculate_coordinate_by_x(s, 0)  # 根据x计算y
        except TypeError:
            actual_y1, actual_y2 = 0, 0
        design_y1, design_y2 = self.dod.calculate_coordinate_by_x(s, 0)  # 根据x计算y

        return [
            [actual_y1, actual_y2],
            [design_y1, design_y2]
        ]


if __name__ == '__main__':
    '''
    倾限值计算函数：cal_bias()
    请翻上去查看
    '''
    #write_circle_center(config.s_excel_path, 'data/圆心.xlsx')
    # write_file()
    res = read_file()
    print('共有{}/{}个点倾限'.format(res[0], res[1]))
    # draw_circles(-1570)
